package com.xinqi.lover.demo;

import com.xinqi.lover.bigmodel.*;
import com.xinqi.lover.bigmodel.RoleContent;
import com.xinqi.lover.utils.AuthUrlUtils;
import com.xinqi.lover.utils.HistoryUtils;
import com.xinqi.lover.utils.WebSocketUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;

/**
 * https://www.xfyun.cn/doc/spark/Web.html
 * https://console.xfyun.cn/services/bm2
 * https://xinghuo.xfyun.cn/sparkapi?ch=jK5dWZO
 */

@Slf4j
public class BigModelNew extends WebSocketListener {
    private static final String pathUrl = "D:\\start_java\\XinQiUtilsOrDemo\\GPTForMyGirlfriend\\secretValue.properties";

    // 地址与鉴权信息  https://spark-api.xf-yun.com/v1.1/chat   1.5地址  domain参数为general
    // 地址与鉴权信息  https://spark-api.xf-yun.com/v2.1/chat   2.0地址  domain参数为generalv2
    public static final String hostUrl = "https://spark-api.xf-yun.com/v2.1/chat";

    public static String appid;
    public static String apiSecret;
    public static String apiKey;

    static {
        // 首先 , 创建 Properties 类对象
        Properties prop = new Properties();
        try {
            // 然后 , 创建 文件输入流 , 读取指定的 properties 配置文件
            FileInputStream input = new FileInputStream(pathUrl);
            // 再后 , 调用 Properties 实例对象的 load 函数 , 加载 properties 配置文件 的 文件输入流
            prop.load(input);
            // 最后 , 调用 Properties 实例对象的 getProperty 函数 , 获取指定 键 对应的 值
            apiKey = prop.getProperty("xinqi.apiKey");
            apiSecret = prop.getProperty("xinqi.apiSecret");
            appid = prop.getProperty("xinqi.appid");
            log.debug(apiKey);
            log.debug(apiSecret);
            log.debug(appid);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static List<RoleContent> historyList = new ArrayList<>(); // 对话历史存储集合

    public static String totalAnswer = ""; // 大模型的答案汇总

    // 环境治理的重要性  环保  人口老龄化  我爱我的祖国
    public static String NewQuestion = "";

    public static final Gson gson = new Gson();


    private static Boolean totalFlag = true; // 控制提示用户是否输入

    // 主函数
    public static void main(String[] args) throws Exception {
        // 个性化参数入口，如果是并发使用，可以在这里模拟
        Scanner scanner = new Scanner(System.in);

        while (true) {

            if (!totalFlag) {
                Thread.sleep(200);
                continue;
            }

            System.out.print("我：");
            totalFlag = false;
            NewQuestion = scanner.nextLine();
            // 构建鉴权url
            String authUrl = AuthUrlUtils.getAuthUrl(hostUrl, apiKey, apiSecret);

            String url = authUrl.toString().replace("http://", "ws://").replace("https://", "wss://");

            Request request = new Request.Builder().url(url).build();

            OkHttpClient client = new OkHttpClient.Builder().build();

            totalAnswer = "";
            //String在运行期有没有限制呢，答案是有的，就是我们前文提到的那个Integer.MAX_VALUE ，这个值约等于4G，在运行期，如果String的长度超过这个范围，就可能会抛出异常。
            WebSocket webSocket = client.newWebSocket(request, new BigModelNew());//这里的第一份参数本来是userId
            log.debug("webSocket1 is " + webSocket);//这里和webSocket2是同一个这里是不可以回收的，因为如果回收了，那么websocket就没了，无法回复消息
        }
    }

    @Override
    public void onOpen(WebSocket webSocket, Response response) {
        super.onOpen(webSocket, response);
        System.out.print("大模型：");
        sendMessage(webSocket);
    }

    private void sendMessage(WebSocket webSocket) {
        try {
            JSONObject requestJson = new JSONObject();

            JSONObject header = new JSONObject();  // header参数-header部分
            header.put("app_id", this.appid);
            header.put("uid", UUID.randomUUID().toString().substring(0, 10));

            JSONObject parameter = new JSONObject(); // parameter参数-parameter.chat部分
            JSONObject chat = new JSONObject();
            chat.put("domain", "generalv2");
            chat.put("temperature", 0.5);
            chat.put("max_tokens", 2048);//https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
//                chat.put("max_tokens", 4096);
            parameter.put("chat", chat);

            JSONObject payload = new JSONObject(); // payload参数
            JSONObject message = new JSONObject();
            JSONArray text = new JSONArray();

            // 历史问题获取
            if (historyList.size() > 0) {
                for (RoleContent tempRoleContent : historyList) {
                    text.add(JSON.toJSON(tempRoleContent));
                }
            }
            // 最新问题
            RoleContent roleContent = new RoleContent();
            roleContent.setRole("user");
            roleContent.setContent(NewQuestion);
            text.add(JSON.toJSON(roleContent));
            historyList.add(roleContent);
            message.put("text", text);
            payload.put("message", message);

            requestJson.put("header", header);
            requestJson.put("parameter", parameter);
            requestJson.put("payload", payload);
            // System.err.println(requestJson); // 可以打印看每次的传参明细
            webSocket.send(requestJson.toString());


            log.debug("webSocket2 is " + webSocket);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * text 是1.4 接口响应里的payload.
     *
     * @param webSocket
     * @param text
     */
    @Override
    public void onMessage(WebSocket webSocket, String text) {
        // System.out.println(userId + "用来区分那个用户的结果" + text);
        JsonParse myJsonParse = gson.fromJson(text, JsonParse.class);
        if (myJsonParse.getHeader().getCode() != 0) {
            System.out.println("发生错误，错误码为：" + myJsonParse.getHeader().getCode());
            System.out.println("本次请求的sid为：" + myJsonParse.getHeader().getSid());
            log.debug("webSocket3 is " + webSocket);

            WebSocketUtils.closeAndGC(webSocket);
            //这个是最后一个，什么是最后一个？就是gpt回复你的问题可能分多次返回，于是因此多次返回前，输入的问题是不会被被gpt回答的。
            // 这里可以做一个接收中途的问题到一个队列里面，当之前的回答都打完了就从该队列里获取问题继续相应回答。
            //不过这里也可以不去做一个队列，直接就是阻塞。通过totalFlag做阻塞。
            totalFlag = true;
        }

        List<Text> textList = myJsonParse.getPayload().getChoices().getText();

        for (Text temp : textList) {
//            System.out.println("answer is "+ temp.getContent());//这个应该是是输出
            System.out.print(temp.getContent());//这里是输出
            totalAnswer += temp.getContent();
        }

        //totalAnswer为最后的结果。

        if (myJsonParse.getHeader().getStatus() == 2) {
            // 可以关闭连接，释放资源
            System.out.println();
            System.out.println("*************************************************************************************");

            RoleContent roleContent = new RoleContent("assistant", totalAnswer);

            //如果size不足以12000，那么可以继续走：
            if (!HistoryUtils.canAddHistory(historyList)) historyList.remove(0);

            historyList.add(roleContent);//这里是添加gpt的回复。

            WebSocketUtils.closeAndGC(webSocket);

            //这个是最后一个，什么是最后一个？就是gpt回复你的问题可能分多次返回，于是因此多次返回前，输入的问题是不会被被gpt回答的。
            // 这里可以做一个接收中途的问题到一个队列里面，当之前的回答都打完了就从该队列里获取问题继续相应回答。
            //不过这里也可以不去做一个队列，直接就是阻塞。通过totalFlag做阻塞。
            totalFlag = true;
            log.debug("结束了1。。。。。");

        }

    }

    @Override
    public void onFailure(WebSocket webSocket, Throwable t, Response response) {
        super.onFailure(webSocket, t, response);
        try {
            if (null != response) {
                int code = response.code();
                System.out.println("onFailure code:" + code);
                System.out.println("onFailure body:" + response.body().string());
                if (101 != code) {
                    System.out.println("connection failed");
                    System.exit(0);
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


}